<#include "license.ftl">
<@license/>
<#assign object = doc.object>
package ${object.@package}.gwt.mvp.service.base;

import redora.client.mvp.ClientFactory;
<#if object.trashcan == "true">
import redora.client.mvp.DeleteEvent;
import redora.client.validation.BusinessRuleViolation;
</#if>
import redora.client.mvp.ConnectEvent;
import com.google.gwt.http.client.*;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import redora.client.mvp.ServiceBase;
import redora.client.util.Field;
import redora.client.util.QueryBuilder;
import ${object.@package}.gwt.model.*;
import ${object.@package}.gwt.model.fields.${object.@name}Fields;

import java.util.logging.Logger;

import static redora.client.Persistable.Scope.Form;
import static com.google.gwt.http.client.RequestBuilder.POST;
import static com.google.gwt.http.client.Response.SC_OK;
import static com.google.gwt.json.client.JSONParser.parseLenient;
import static java.util.logging.Level.*;
import static redora.client.util.GWTViewUtil.displayError;
import static redora.client.util.GWTViewUtil.displayInfo;


/**
 * See {@link ${object.@package}.gwt.mvp.service.${object.@name}Service} for how to use the service object.
 * ${object.@name}ServiceBase contains the Redora generated standard services and the ${object.@name} cache.
 *
 * @author Redora (www.redora.net)
 * @see ${object.@package}.gwt.mvp.service.${object.@name}Service}
 */
public abstract class ${object.@name}ServiceBase extends ServiceBase<${object.@name}> {
    static Logger l = Logger.getLogger("${object.@name}ServiceBase");

    protected ${object.@name}ServiceBase(ClientFactory clientFactory) {
        super(clientFactory, ${object.@name}.class);
    }

    @Override
    protected void buildRequest(QueryBuilder builder, ${object.@name} pojo) {
        for (Field field : pojo.dirty.keySet()) {
<#assign elseThing = "">
<#list object.attributes?children as att>
    <#if att?node_type == "element" && att?node_name != "object"  && att?node_name != "set">
            ${elseThing}if (field == ${object.@name}Fields.${att.@fieldName}) {
                builder.addQuery(${object.@name}Fields.${att.@fieldName}.name, pojo.get${att.@fieldName?cap_first}());
        <#assign elseThing = "} else ">
    </#if>
</#list>
            } else {
                l.log(SEVERE, "Unknown ${object.@name} field " + field.name);
            }
        }
    }

    @Override
    public void trash(final ${object.@name} pojo) {
    <#if object.trashcan == "true">
        String _url = URL.encode(baseTrashUrl);
        final QueryBuilder queryBuilder = new QueryBuilder();
        queryBuilder.addQuery("_operationType", "trash");
        queryBuilder.addQuery("id", pojo.getId());

        l.log(INFO, "Ready to sent trash request: " + queryBuilder.toString());
        RequestBuilder builder = new RequestBuilder(POST, _url);
        builder.setHeader("Content-Type", "application/x-www-form-urlencoded");
        String requestData = queryBuilder.toString();
        builder.setRequestData(requestData + getAuthInfo());
        builder.setCallback(new RequestCallback() {
            public void onError(Request request, Throwable e) {
                l.log(SEVERE, "Failed to submit trash: " + queryBuilder.toString(), e);
                displayError(redoraMessages.responseErrorConnect());
            }

            public void onResponseReceived(Request request, Response response) {
                if (response.getStatusCode() == SC_OK) {
                    JSONObject responseJSON = parseLenient(response.getText()).isObject().get("response").isObject();
                    int status = (int) responseJSON.get("status").isNumber().doubleValue();
                    l.log(INFO, "Response received " + responseJSON.get("status"));
                    l.log(FINE, responseJSON.toString());
                    if (customResponse(status, responseJSON)) {
                        if (ResponseStatus.values()[status] == ResponseStatus.success) {
                            l.log(INFO, "Clearing finder cache");
                            listMap.clear();
                            clientFactory.getEventBus().fireEvent(new DeleteEvent(cls, pojo));
                            l.log(INFO, "Trash successful, fired DeleteEvent for " + pojo);
                            displayInfo(redoraMessages.recordDeleted());
                        } else {
                            JSONArray array = responseJSON.get("violations").isArray();
                            BusinessRuleViolation[] violations = new BusinessRuleViolation[array.size()];
                            for (int i = 0; i < array.size(); i++) {
                                violations[i] = BusinessRuleViolation.from(array.get(i).isObject(), clientFactory);
                                l.log(INFO, "Added violation " + violations[i]);
                            }
                            clientFactory.getEventBus().fireEvent(new DeleteEvent(cls, pojo, violations));
                            l.log(INFO, "Fired DeleteEvent with " + violations.length + " business rule violation(s).");
                        }
                    }
                } else {
                    l.log(SEVERE, "Retrieved unwanted response " + response.getStatusText() + " at trashing " + pojo);
                    displayError(redoraMessages.responseErrorNot200());
                }
            }
        });
        try {
            builder.send();
        } catch (RequestException e) {
            l.log(SEVERE, "Failed to trash", e);
            displayError(redoraMessages.responseErrorConnect());
        }
    </#if>
    }

    <#list doc["/object/attributes/set[@multiplicity='n-to-m']"] as att>
    <#assign finderName = att.@theirName>
    <#if att.@class == object.@name>
    <#assign finderName = att.@myName>
    </#if>

    public void connect${att.@class}(final Long ${att.@myName}Id, final Long ${att.@theirName}Id) {
        final String _url = URL.encode(basePersistUrl);

        final QueryBuilder queryBuilder = new QueryBuilder();
        queryBuilder.addQuery("_operationType", "connectTo${att.@class?cap_first}");
        queryBuilder.addQuery("${att.@myName}Id", ${att.@myName}Id);
        queryBuilder.addQuery("${att.@theirName}Id", ${att.@theirName}Id);

        l.log(INFO, "Ready to sent connect${att.@class} request: " + queryBuilder.toString());
        RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, _url);
        builder.setHeader("Content-Type", "application/x-www-form-urlencoded");
        String requestData = queryBuilder.toString();
        builder.setRequestData(requestData + getAuthInfo());
        builder.setCallback(new RequestCallback() {
            public void onError(Request request, Throwable e) {
                l.log(SEVERE, "Failed to submit connect${att.@class}: " + queryBuilder.toString(), e);
                displayError(redoraMessages.responseErrorConnect());
            }

            public void onResponseReceived(Request request, Response response) {
                if (response.getStatusCode() == SC_OK) {
                    JSONObject responseJSON = parseLenient(response.getText()).isObject().get("response").isObject();
                    int status = (int) responseJSON.get("status").isNumber().doubleValue();
                    l.log(INFO, "Response received " + responseJSON.get("status"));
                    l.log(FINE, responseJSON.toString());
                    if (customResponse(status, responseJSON)) {
                        if (ResponseStatus.values()[status] == ResponseStatus.success) {
                            l.log(INFO, "Clearing finder cache");
                            listMap.clear();
                            displayInfo("Add relation with ${object.@name}:" + ${att.@myName}Id + " and ${att.@class}:" + ${att.@theirName}Id);
                            Long[] idParams = {${att.@myName}Id, ${att.@theirName}Id};
                            clientFactory.getEventBus().fireEvent(new ConnectEvent(${object.@name}.class, ${att.@class}.class, ConnectEvent.ActionType.connect, idParams));
                        } else {
                            l.log(SEVERE, "Unexpected response status " + responseJSON.get("status").isString().stringValue() + " from " + _url);
                            displayError(redoraMessages.responseErrorNot200());
                        }
                    }
                } else {
                    l.log(SEVERE, "Retrieved unwanted response " + response.getStatusText() + " at deleteRelationWith${att.@class} ");
                    displayError(redoraMessages.responseErrorNot200());
                }
            }
        });
        try {
            builder.send();
        } catch (RequestException e) {
            l.log(SEVERE, "Failed to connect${att.@class}", e);
            displayError(redoraMessages.responseErrorConnect());
        }
    }


    public void deleteRelationWith${att.@class}(final Long ${att.@myName}Id, final Long ${att.@theirName}Id) {
        final String _url = URL.encode(baseDeleteUrl);

        final QueryBuilder queryBuilder = new QueryBuilder();
        queryBuilder.addQuery("_operationType", "deleteRelationWith${att.@class}");
        queryBuilder.addQuery("${att.@myName}Id", ${att.@myName}Id);
        queryBuilder.addQuery("${att.@theirName}Id", ${att.@theirName}Id);

        l.log(INFO, "Ready to sent deleteRelationWith${att.@class} request: " + queryBuilder.toString());
        RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, _url);
        builder.setHeader("Content-Type", "application/x-www-form-urlencoded");
        String requestData = queryBuilder.toString();
        builder.setRequestData(requestData + getAuthInfo());
        builder.setCallback(new RequestCallback() {
            public void onError(Request request, Throwable e) {
                l.log(SEVERE, "Failed to submit deleteRelationWith${att.@class}: " + queryBuilder.toString(), e);
                displayError(redoraMessages.responseErrorConnect());
            }

            public void onResponseReceived(Request request, Response response) {
                if (response.getStatusCode() == SC_OK) {
                    JSONObject responseJSON = parseLenient(response.getText()).isObject().get("response").isObject();
                    int status = (int) responseJSON.get("status").isNumber().doubleValue();
                    l.log(INFO, "Response received " + responseJSON.get("status"));
                    l.log(FINE, responseJSON.toString());
                    if (customResponse(status, responseJSON)) {
                        if (ResponseStatus.values()[status] == ResponseStatus.success) {
                            l.log(INFO, "Clearing finder cache");
                            listMap.clear();
                            displayInfo("Delete relation with ${object.@name}:" + ${att.@myName}Id + " and ${att.@class}:" + ${att.@theirName}Id);
                            Long[] idParams = {${att.@myName}Id, ${att.@theirName}Id};
                            clientFactory.getEventBus().fireEvent(new ConnectEvent(${object.@name}.class, ${att.@class}.class, ConnectEvent.ActionType.disConnect, idParams));
                        } else {
                            l.log(SEVERE, "Unexpected response status " + responseJSON.get("status").isString().stringValue() + " from " + _url);
                            displayError(redoraMessages.responseErrorNot200());
                        }
                    }
                } else {
                    l.log(SEVERE, "Retrieved unwanted response " + response.getStatusText() + " at deleteRelationWith${att.@class} ");
                    displayError(redoraMessages.responseErrorNot200());
                }
            }
        });
        try {
            builder.send();
        } catch (RequestException e) {
            l.log(SEVERE, "Failed to deleteRelationWith${att.@class}", e);
            displayError(redoraMessages.responseErrorConnect());
        }
    }


    </#list>

    @Override
    public ${object.@name} cache(Long id) {
        return (id == null || !pojoMap.containsKey(id)) ? new ${object.@name}(Form) : pojoMap.get(id);
    }
}